package com.baturu.vin.hyundai.service.impl;

import com.baturu.vin.base.dto.BasePartsImageInfoDTO;
import com.baturu.vin.base.exception.EPCDataNotStandardException;
import com.baturu.vin.base.service.EPCVinWaitCacheService;
import com.baturu.vin.base.service.impl.EPCAnalyser;
import com.baturu.vin.dto.*;
import com.baturu.vin.dto.parts.PartsAssemblyDTO;
import com.baturu.vin.hyundai.constant.HyundaiKiaSource;
import com.baturu.vin.hyundai.constant.HyundaiKiaVariables;
import com.baturu.vin.hyundai.dal.dao.*;
import com.baturu.vin.hyundai.dal.entity.HyundaiKiaFeatureEntity;
import com.baturu.vin.hyundai.dto.*;
import com.baturu.vin.kit.StreamKit;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service("hyundaiKiaEPCAnalyser")
public class HyundaiEPCAnalyser extends EPCAnalyser<HyundaiKiaVehicleDTO, HyundaiKiaPartsMappingDTO, KiaPartsDTO, EPCCarTypeDTO> implements InitializingBean {

    @Autowired
    private HyundaiKiaPartsMappingDAO partsMapping;
    @Autowired
    private EPCVinWaitCacheService epcVinCacheService;
    @Autowired
    private HyundaiKiaVehicleDAO vehicleDAO;
    @Autowired
    private KiaPartsDAO kiaPartsDAO;
    @Autowired
    private HyundaiKiaCarAnalyser carAnalyser;
    @Autowired
    private HyundaiKiaImageDAO hyundaiKiaImageDAO;
    @Autowired
    private LJDVinSerivce ljdVinSerivce;

    @Autowired
    private HyundaiBtrPartsDAO hyundaiBtrPartsDAO;

    private String BASE_IMAGE_URL;
    /**
     * 图片压缩比例
     */
    private final static float RATE = 0.677f;
    @Autowired
    private HyundaiKiaFeatureDAO featureDAO;
    @Autowired
    private HyundaiKiaAssemblyDAO hyundaiKiaAssemblyDAO;

    @Override
    public HyundaiKiaVehicleDTO analyseVinCode(String vinCode) {
        HyundaiKiaVehicleDTO vehicleDTO = vehicleDAO.findVehicle(vinCode);
        if (null == vehicleDTO) {
            epcVinCacheService.addWaitCacheVinCode(EPCAnalyserEnum.HYUNDAI, vinCode);
            return null;
        }

        // 特殊处理逻辑现代起亚因为爬取数据限制，需要更新数据通过版本号控制，并且刷新接口有操作限制
        if (!HyundaiKiaVariables.VEHICLE_VERSION.equals(vehicleDTO.getVersion())) {
            Set<String> failVinCode = epcVinCacheService.reCrawlingVinCode(Sets.newHashSet(vinCode));
            if (CollectionUtils.isNotEmpty(failVinCode)) {
                epcVinCacheService.addWaitCacheVinCode(EPCAnalyserEnum.HYUNDAI, vinCode);
            }
        }
        return vehicleDTO;
    }

    @Override
    public HyundaiKiaVehicleDTO analyseVinCodeByCache(String vinCode) {
        return vehicleDAO.findVehicle(vinCode);
    }

    @Override
    public Set<String> filterEPCPartsNamesByVinCode(String vinCode, Set<String> epcPartsNames) {
        HyundaiKiaVehicleDTO hyundaiKiaVehicleDTO = analyseVinCode(vinCode);
        if (hyundaiKiaVehicleDTO == null) {
            return Collections.emptySet();
        }
        List<HyundaiKiaPartsMappingDTO> hyundaiKiaPartsMappingDTOS = queryPartsMappings(epcPartsNames, hyundaiKiaVehicleDTO);
        List<KiaPartsDTO> kiaPartsDTOS = queryPartsInfoByPartsMapping(hyundaiKiaPartsMappingDTOS, hyundaiKiaVehicleDTO);
        return kiaPartsDTOS.stream().map(KiaPartsDTO::getFinalPartName).collect(Collectors.toSet());
    }

    @Override
    public List<EPCCarTypeDTO> queryCarTypes(HyundaiKiaVehicleDTO analyseResult) {
        if (null == analyseResult) {
            log.warn("vin信息为空");
            return Collections.emptyList();
        }

        try {
            String btrId = carAnalyser.analysisBtrIdAndBuildCarType(analyseResult);
            EPCCarTypeDTO epcCarTypeDTO = new EPCCarTypeDTO();
            epcCarTypeDTO.setBtrId(btrId);
            epcCarTypeDTO.setExtendAttrDTOs(Lists.newArrayList(new EPCCarTypeDTO.ExtendAttributeDTO("bpno", analyseResult.getBpno(), "碰撞部位需要")));
            log.info("hyundai,vin:{},btrid{}", analyseResult.getVinCode(), btrId);
            return Lists.newArrayList(epcCarTypeDTO);
        } catch (EPCDataNotStandardException e) {
            log.error("可能存在不符合规范数据，请排查！", e);
            return Collections.emptyList();
        } catch (Exception e) {
            log.error("未知异常，请排查！", e);
            return Collections.emptyList();
        }
    }

    @Override
    public List<HyundaiKiaPartsMappingDTO> queryPartsMappings(Set<String> btrPartsNames, HyundaiKiaVehicleDTO analyseResult) {
        if (null == analyseResult) {
            return Collections.emptyList();
        }
        return StreamKit.partitionStream(btrPartsNames, HyundaiKiaSource.LIMIT_BTRPARTSNAME_COUNT)
                .flatMap(l -> partsMapping.queryByBtrPartsName(analyseResult.getBpno(), l).stream()).distinct()
                .collect(Collectors.toList());
    }

    @Override
    public List<KiaPartsDTO> queryPartsInfoByPartsMapping(List<HyundaiKiaPartsMappingDTO> partsMappings, HyundaiKiaVehicleDTO analyseResult) {
        Set<String> partsCodes = partsMappings.stream()
                .map(HyundaiKiaPartsMappingDTO::getPartsCode)
                .collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(partsCodes)) {
            return Collections.emptyList();
        }
        return queryPartsInfoByPartsMappingV2(partsMappings, analyseResult);
    }

    /**
     * 配件信息用于跟主名映射关联的Key，各品牌如有需要自行覆盖，注意联动 getPartsGroupKeyForPartsMappingFunction
     */
    @Override
    protected Function<KiaPartsDTO, String> getPartsGroupKeyForPartsMappingFunction() {
        return KiaPartsDTO::getPartsId;
    }

    private List<KiaPartsDTO> queryPartsInfoByPartsMappingV2(List<HyundaiKiaPartsMappingDTO> partsMappings, HyundaiKiaVehicleDTO analyseResult) {
        if (Objects.isNull(analyseResult)) {
            return Collections.emptyList();
        }
        //KMH的vin先用v1走一遍
        if (analyseResult.getVinCode().startsWith("KMH")) {
            List<KiaPartsDTO> partsDTOS = StreamKit.partitionStream(partsMappings, SEGMENT_QUERY_SIZE)
                    .flatMap(d -> kiaPartsDAO.queryByVinAndPartsMapping(d).stream())
                    .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(partsDTOS)) {
                return partsDTOS;
            }
        }
        List<KiaPartsDTO> kiaPartsDTOS = ljdVinSerivce.queryLJDPartsByPartsMappingV2(partsMappings, analyseResult);
        if (CollectionUtils.isNotEmpty(kiaPartsDTOS)) {
            analyseResult.setAccurate(true);
            return kiaPartsDTOS;
        }
        return StreamKit.partitionStream(partsMappings, SEGMENT_QUERY_SIZE)
                .flatMap(d -> kiaPartsDAO.queryByVinAndPartsMapping(d).stream())
                .collect(Collectors.toList());
    }

    private List<KiaPartsDTO> queryPartsInfoByPartsMappingV1(List<HyundaiKiaPartsMappingDTO> partsMappings, HyundaiKiaVehicleDTO analyseResult) {
        if (Objects.isNull(analyseResult)) {
            return Collections.emptyList();
        }

        return StreamKit.partitionStream(partsMappings, SEGMENT_QUERY_SIZE)
                .flatMap(d -> kiaPartsDAO.queryByVinAndPartsMapping(d).stream())
                .collect(Collectors.toList());
    }

    @Override
    public List<KiaPartsDTO> queryPartsInfoByPartsCode(Set<String> partsCodes) {
        return StreamKit.partitionStream(partsCodes, SEGMENT_QUERY_SIZE)
                .flatMap(d -> kiaPartsDAO.queryByPartsCode(d).stream())
                .collect(Collectors.toList());
    }

    @Override
    protected Map<String, List<KiaPartsDTO>> filterPartsInfoByVinAnalyseResult(Map<String, List<KiaPartsDTO>> partsInfoGroupByPartsName, HyundaiKiaVehicleDTO analyseResult) {
        if (!analyseResult.getAccurateFlag()) {
            //V1带出的多编码用V2去带如果带出唯一则替换V1结果
//            Set<String> btrPartsNames = partsInfoGroupByPartsName.entrySet().stream()
//                    .filter(d -> d.getValue().size() > 1)
//                    .map(Map.Entry::getKey).collect(Collectors.toSet());
//            Map<String, List<KiaPartsDTO>> partsInfoGroupByPartsNameV2= queryByV2(btrPartsNames, analyseResult);
//            return mergeMap(partsInfoGroupByPartsName, partsInfoGroupByPartsNameV2);
            return partsInfoGroupByPartsName;
        }
        //用v2带出多个编码的主名的用v1走一遍
        Set<String> btrPartsName = partsInfoGroupByPartsName.entrySet().stream()
                .filter(d -> d.getValue().stream().map(KiaPartsDTO::getFinalPartsCode).distinct().count() > 1)
                .map(Map.Entry::getKey)
                .collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(btrPartsName)) {
            return partsInfoGroupByPartsName;
        }
        List<HyundaiKiaPartsMappingDTO> hyundaiKiaPartsMappingDTOS = queryPartsMappings(btrPartsName, analyseResult);
        List<KiaPartsDTO> kiaPartsDTOS = queryPartsInfoByPartsMappingV1(hyundaiKiaPartsMappingDTOS, analyseResult);
        analyseResult.setAccurate(false);
        kiaPartsDTOS = filterPartsInfoByVinAnalyseResult(kiaPartsDTOS, analyseResult);
        Map<String, List<KiaPartsDTO>> partsInfoGroupByPartsNameV1 = getPartsInfoGroupByPartsNameForCall(kiaPartsDTOS, hyundaiKiaPartsMappingDTOS);
        btrPartsName.forEach(d ->{
            List<KiaPartsDTO> v1KiaPartsDTOS = partsInfoGroupByPartsNameV1.get(d);
            //优选feature符合个数最多的
            if (CollectionUtils.isNotEmpty(v1KiaPartsDTOS) && v1KiaPartsDTOS.size() > 1) {
                compareNum(v1KiaPartsDTOS);
            }
            if (CollectionUtils.isNotEmpty(v1KiaPartsDTOS)) {
                partsInfoGroupByPartsName.put(d,v1KiaPartsDTOS);
            }
        });
        return partsInfoGroupByPartsName;
    }

    private Map<String, List<KiaPartsDTO>> mergeMap(Map<String, List<KiaPartsDTO>> partsInfoGroupByPartsName, Map<String, List<KiaPartsDTO>> partsInfoGroupByPartsNameV2) {
        partsInfoGroupByPartsNameV2.forEach((k,v)->{
            List<KiaPartsDTO> partsDTOS = partsInfoGroupByPartsName.getOrDefault(k, Lists.newArrayList());
            if (partsDTOS.size()>1 && v.size() ==1 ) {
                partsInfoGroupByPartsName.put(k, partsDTOS);
            }
        });
        return partsInfoGroupByPartsName;
    }

    private Map<String, List<KiaPartsDTO>> queryByV2(Set<String> btrPartsNames, HyundaiKiaVehicleDTO analyseResult) {
        List<HyundaiKiaPartsMappingDTO> hyundaiKiaPartsMappingDTOS = queryPartsMappings(btrPartsNames, analyseResult);
        List<KiaPartsDTO> kiaPartsDTOS = ljdVinSerivce.queryLJDPartsByPartsMappingV2(hyundaiKiaPartsMappingDTOS, analyseResult);
        return getPartsInfoGroupByPartsNameForCall(kiaPartsDTOS, hyundaiKiaPartsMappingDTOS);
    }

    @Override
    public List<KiaPartsDTO> queryPartsInfoByPartsCode(Set<String> partsCodes, HyundaiKiaVehicleDTO analyseResult) {
        if (Objects.isNull(analyseResult)) {
            return Collections.emptyList();
        }
        List<KiaPartsDTO> kiaPartsDTOS = ljdVinSerivce.queryLJDParts(partsCodes, analyseResult);
        if (CollectionUtils.isNotEmpty(kiaPartsDTOS)) {
            analyseResult.setAccurate(true);
            return kiaPartsDTOS;
        }
        return StreamKit.partitionStream(partsCodes, SEGMENT_QUERY_SIZE)
                .flatMap(d -> kiaPartsDAO.queryByBpnoAndPartsCode(analyseResult.getBpno(), d).stream())
                .collect(Collectors.toList());
    }

    @Override
    public List<KiaPartsDTO> filterPartsInfoByVinAnalyseResult(List<KiaPartsDTO> partsInfo, HyundaiKiaVehicleDTO analyseResult) {
        if (analyseResult.getAccurateFlag() || CollectionUtils.isEmpty(partsInfo)) {
            return partsInfo;
        }
        //日期过滤
        partsInfo.removeIf(d -> !filterByDate(analyseResult.getMakeDate(), d.getStartTime(), d.getEndTime()));
        //过滤majorAttribute
        Set<String> vehicleConditions = analyseResult.getMajorAttributeJson().stream().map(HyundaiAttributeEntity::getDescribe).collect(Collectors.toSet());
        Set<String> chVehicleConditions = analyseResult.getMajorAttributeJson().stream().map(HyundaiAttributeEntity::getDescribe).map(this::changeCh).collect(Collectors.toSet());
        partsInfo.removeIf(d -> !(filterByPartsNote(d, vehicleConditions) || filterByPartsNote(d, chVehicleConditions)));
        partsInfo = filerByRHD(partsInfo);
        //过滤feature
        List<HyundaiKiaFeatureEntity> features = analyseResult.getFeatureCodesJson();
        if (CollectionUtils.isNotEmpty(features)) {
            Set<String> featureCodes = features.stream().map(HyundaiKiaFeatureEntity::getCode).collect(Collectors.toSet());
            //过滤国家代号
            partsInfo.removeIf(d -> filterFeatureByAreaNum(analyseResult.getAreaNum(), d));
            //过滤feature,排除国家代号
            partsInfo.removeIf(d -> filterByFeatureCode(analyseResult.getAreaNum(), featureCodes, d));
        }
        partsInfo = filterByColor(analyseResult, partsInfo);
        return partsInfo;
    }

    private List<KiaPartsDTO> filerByRHD(List<KiaPartsDTO> partsInfo) {
        List<KiaPartsDTO> filterRHD = partsInfo.stream().filter(d -> !StringUtils.contains(d.getFinalPartsNote(), "RHD")).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(filterRHD)) {
            return partsInfo;
        }
        return filterRHD;
    }

    private List<KiaPartsDTO> filterByColor(HyundaiKiaVehicleDTO analyseResult, List<KiaPartsDTO> partsInfo) {
        String interiorCode = analyseResult.getInteriorCode();
        String exteriorCode = analyseResult.getExteriorCode();
        if (!StringUtils.isNoneBlank(interiorCode, exteriorCode)) {
            return partsInfo;
        }
        List<KiaPartsDTO> filterByColor = partsInfo.stream().filter(d -> StringUtils.containsAny(d.getEpcRemark(), interiorCode, exteriorCode)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(filterByColor)) {
            return partsInfo;
        }
        return filterByColor;
    }

    private String changeCh(String d) {
        if (d.equals("HIGH GRADE")) {
            return "高配";
        }
        return d;
    }

    public List<KiaPartsDTO> filterGroup(List<KiaPartsDTO> partsInfo, HyundaiKiaVehicleDTO analyseResult) {
        //日期过滤
        partsInfo.removeIf(d -> !filterByDate(analyseResult.getMakeDate(), d.getStartTime(), d.getEndTime()));
        //过滤majorAttribute
        Set<String> vehicleConditions = analyseResult.getMajorAttributeJson().stream().map(HyundaiAttributeEntity::getDescribe).collect(Collectors.toSet());
        partsInfo.removeIf(d -> !filterByPartsNote(d, vehicleConditions));
        //过滤feature
        List<HyundaiKiaFeatureEntity> features = analyseResult.getFeatureCodesJson();
        if (CollectionUtils.isNotEmpty(features)) {
            Set<String> featureCodes = features.stream().map(HyundaiKiaFeatureEntity::getCode).collect(Collectors.toSet());
            partsInfo.removeIf(d -> filterByFeatureCode(analyseResult.getAreaNum(), featureCodes, d));
        }
        return partsInfo;
    }

    private boolean filterFeatureByAreaNum(String areaNum, KiaPartsDTO partsInfo) {
        List<String> featureCode = partsInfo.getFeatureCodesJson().stream()
                .filter(d -> !d.contains("W10"))
                .filter(d -> d.contains("-"))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(featureCode)) {
            return false;
        }
        return featureCode.stream()
                .map(d -> StringUtils.substringAfter(d, "-"))
                .anyMatch(areaNum::startsWith);
    }

    private void compareNum(List<KiaPartsDTO> partsInfo) {
        int vehicleCodeNum = partsInfo.stream().mapToInt(KiaPartsDTO::getFeatureCount).max().getAsInt();
        partsInfo.removeIf(d -> d.getFeatureCount() != vehicleCodeNum);
    }

    private boolean filterByFeatureCode(String areaNum,Set<String> featureCodes, KiaPartsDTO partsInfo) {
        List<String> partsFeature = partsInfo.getFeatureCodesJson().stream()
                .filter(d -> !d.contains("W10")).filter(d-> !areaNum.startsWith(d)).collect(Collectors.toList());
        return !partsFeature.stream()
                .allMatch(d -> containOrStartWith(d, featureCodes, partsInfo));
    }

    private boolean containOrStartWith(String partsFeaterCode, Set<String> featureCodes, KiaPartsDTO partsInfo) {
        boolean contains = featureCodes.contains(partsFeaterCode);
        if(contains){
            partsInfo.increaseFeatureCount();
        }
        return contains || featureCodes.stream().anyMatch(partsFeaterCode::startsWith);
    }


    boolean filterByPartsNote(KiaPartsDTO kiaPartsDTO, Set<String> vehicleConditions) {
        Set<String> partsNotes = kiaPartsDTO.getFinalPartsNoteList()
                .stream()
                .map(String::trim)
                .collect(Collectors.toSet());
        return compareSet(partsNotes, vehicleConditions);
    }

    private boolean compareSet(Set<String> partsNotes, Set<String> vehicleInfo) {
        Set<String> drivePos = Sets.newHashSet("LEFT HAND DRIVE", "RIGHT HAND DRIVE", "DRIVE TYPE - RHD", "DRIVE TYPE - LHD");
        boolean flag = false;
        for (String partsNote : partsNotes) {
            if (isFilter(partsNote, vehicleInfo, drivePos)) {
                continue;
            }
            if (isDrive(partsNote, drivePos)) {
                if (isMatchDrive(drivePos, vehicleInfo)) {
                    flag = true;
                }
            } else {
                for (String vehicle : vehicleInfo) {
                    if (vehicle.contains(partsNote)) {
                        flag = true;
                        break;
                    } else {
                        flag = false;
                    }
                }
            }
            if (!flag) {
                return false;
            }
        }
        return true;
    }

    /**
     * 临时去掉两个过滤条件,现在还没有数据
     *
     * @param partsNote
     * @param vehicleInfo
     * @return
     */
    private boolean isFilter(String partsNote, Set<String> vehicleInfo, Set<String> drivePos) {
        if (isDrive(partsNote, drivePos)) {
            if (!isMatchDrive(drivePos, vehicleInfo)) {
                return true;
            }
        } else if (isSellArea(partsNote)) {
            Set<String> sellAreas = vehicleInfo.stream()
                    .filter(this::isSellArea)
                    .collect(Collectors.toSet());
            if (CollectionUtils.isEmpty(sellAreas)) {
                return true;
            }
        }
        return false;
    }

    private boolean isDrive(String partsNote, Set<String> drivePos) {
        return drivePos.contains(partsNote);
    }

    private boolean isMatchDrive(Set<String> drivePos, Set<String> vehicleInfo) {
        ImmutableSet<String> immutableSet = Sets.intersection(drivePos, vehicleInfo).immutableCopy();
        return CollectionUtils.isNotEmpty(immutableSet);
    }

    private boolean isSellArea(String partsNote) {
        return StringUtils.containsIgnoreCase(partsNote, "WEATHER") && StringUtils.containsIgnoreCase(partsNote, "ZONE");
    }

    boolean filterByDate(String carDate, String partsStartDate, String partsEndDate) {
        if (StringUtils.isBlank(carDate)) {
            return true;
        }
        long carTime = Long.parseLong(carDate);
        long startTime = StringUtils.isNotBlank(partsStartDate) ? Long.parseLong(partsStartDate) : 0;
        long endTime = StringUtils.isNotBlank(partsEndDate) ? Long.parseLong(partsEndDate) : 99999999L;
        return carTime <= endTime && carTime >= startTime;
    }

    @Override
    protected Map<String, List<EPCPartsImageDTO>> queryPartsImage(Map<String, List<KiaPartsDTO>> partsInfoGroup, HyundaiKiaVehicleDTO analyseResult) {
        if (Objects.isNull(analyseResult)) {
            return Collections.emptyMap();
        }
        return partsInfoGroup.entrySet().stream()
                .filter(e -> CollectionUtils.isNotEmpty(e.getValue()))
                .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream()
                        .filter(Objects::nonNull)
                        .map(input -> {
                            EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();
//                            epcPartsImageDTO.setImageUrl(BASE_IMAGE_URL + String.format("/illust/GROUP/%s/TIF/%s%s%s.gif", analyseResult.getBpno(), input.getGroupCode(), input.getImgX(), input.getImgY()));
                            epcPartsImageDTO.setImageUrl(BASE_IMAGE_URL + String.format("illust/GROUP/%s/TIF/%s%s%s", analyseResult.getBpno(), input.getGroupCode(), input.getImgX(), input.getImgY()));
                            epcPartsImageDTO.setImageCode(input.getGroupCode());
                            epcPartsImageDTO.setEpcPartsImagePositionDTOs(Lists.newArrayList(buildEPCPartsImagePositionDTO(input)));
                            return epcPartsImageDTO;
                        }).collect(Collectors.toList())));
    }

    @Override
    protected List<HyundaiKiaPartsMappingDTO> queryPartsMappingByPartsInfoAndAnalyseResult(Map<String, List<KiaPartsDTO>> partsInfoGroup, HyundaiKiaVehicleDTO analyseResult) {
        Set<String> partsIds = partsInfoGroup.values().stream()
                .flatMap(l -> l.stream().map(KiaPartsDTO::getPartsId))
                .collect(Collectors.toSet());
        return StreamKit.partitionStream(partsIds, SEGMENT_QUERY_SIZE)
                .flatMap(l -> partsMapping.queryByPartsCode(analyseResult.getBpno(), l).stream())
                .collect(Collectors.toList());
    }

    @Override
    protected List<KiaPartsDTO> queryPartsInfoByImageCode(Set<String> imageCodes, HyundaiKiaVehicleDTO analyseResult) {
        return StreamKit.partitionStream(imageCodes, SEGMENT_QUERY_SIZE)
                .flatMap(l -> hyundaiKiaImageDAO.queryPartCodeByGroupCode(l, analyseResult.getBpno()).stream())
                .collect(Collectors.toList());
    }

    @Override
    protected List<EPCPartsImageDTO> queryEPCImageInfoByImageCode(Set<String> imageCodes, HyundaiKiaVehicleDTO analyseResult) {
        Set<String> imageUrls = StreamKit.partitionStream(imageCodes, SEGMENT_QUERY_SIZE)
                .flatMap(l -> hyundaiKiaImageDAO.queryPartsByGroupCode(l, analyseResult.getBpno()).stream())
                .map(KiaPartsDTO::getFinalPartsImageUrl)
                .collect(Collectors.toSet());

        if (CollectionUtils.isEmpty(imageUrls)) {
            return Collections.emptyList();
        }

        List<KiaPartsDTO> images = StreamKit.partitionStream(imageUrls, SEGMENT_QUERY_SIZE)
                .flatMap(l -> hyundaiKiaImageDAO.queryPointByImageUrl(l).stream())
                .collect(Collectors.toList());

        images = filterPartsInfoByVinAnalyseResult(images, analyseResult);

        return images.stream().collect(Collectors.groupingBy(KiaPartsDTO::getGroupCode)).entrySet().stream().map(entry -> {
            EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();
            epcPartsImageDTO.setImageCode(entry.getKey());
            epcPartsImageDTO.setImageUrl(BASE_IMAGE_URL + entry.getValue().get(0).getFinalPartsImageUrl());
            epcPartsImageDTO.setEpcPartsImagePositionDTOs(entry.getValue().stream().map(this::buildEPCPartsImagePositionDTO).collect(Collectors.toList()));
            return epcPartsImageDTO;
        }).collect(Collectors.toList());
    }

    @Override
    public Map<String, List<BasePartsImageInfoDTO>> queryEPCImageInfo(Set<String> sortNames, HyundaiKiaVehicleDTO analyseResult) {
        List<HyundaiKiaImageInfoDTO> imageInfo = StreamKit.partitionStream(sortNames, SEGMENT_QUERY_SIZE)
                .flatMap(l -> hyundaiKiaImageDAO.queryEPCImageInfo(l).stream())
                .distinct()
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(imageInfo)) {
            return Collections.emptyMap();
        }

        Map<String, List<KiaPartsDTO>> imageGroup = StreamKit.partitionStream(imageInfo, SEGMENT_QUERY_SIZE)
                .flatMap(l -> hyundaiKiaImageDAO.queryPartsByImageInfo(l, analyseResult.getBpno()).stream())
                .collect(Collectors.groupingBy(KiaPartsDTO::getGroupCode));

        return imageInfo.stream()
                .filter(d -> CollectionUtils.isNotEmpty(imageGroup.get(d.getGroupCode())))
                .peek(d -> d.setImageUrl(BASE_IMAGE_URL + getNewImageUrl(imageGroup.get(d.getGroupCode()).get(0).getFinalPartsImageUrl())))
                .collect(Collectors.groupingBy(HyundaiKiaImageInfoDTO::getSortName))
                .entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream().map(d -> (BasePartsImageInfoDTO) d).distinct().collect(Collectors.toList())));
    }

    private String getNewImageUrl(String finalPartsImageUrl) {
        return StringUtils.replace(finalPartsImageUrl.replaceFirst("/", ""), ".gif", "");
    }

    private EPCPartsImagePositionDTO buildEPCPartsImagePositionDTO(KiaPartsDTO kiaPartsDTO) {
        EPCPartsImagePositionDTO epcPartsImagePositionDTO = new EPCPartsImagePositionDTO();
        epcPartsImagePositionDTO.setPosition(kiaPartsDTO.getGroupNumber());

        EPCImagePointDTO topLeft = new EPCImagePointDTO();
        topLeft.setX(compressImage(kiaPartsDTO.getFinalPartsImageX()));
        topLeft.setY(compressImage(kiaPartsDTO.getFinalPartsImageY()));
        EPCImagePointDTO bottomRight = new EPCImagePointDTO();
        bottomRight.setX(compressImage(kiaPartsDTO.getFinalPartsImageX() + kiaPartsDTO.getFinalPartsImageOffsetX()));
        bottomRight.setY(compressImage(kiaPartsDTO.getFinalPartsImageY() + kiaPartsDTO.getFinalPartsImageOffsetY()));

        epcPartsImagePositionDTO.setBottomRight(bottomRight);
        epcPartsImagePositionDTO.setTopLeft(topLeft);
        return epcPartsImagePositionDTO;
    }

    private int compressImage(int position) {
        float p = position * RATE;
        return (int) p;
    }

    @Override
    protected Map<String, String> queryInstallNum(Map<String, List<KiaPartsDTO>> partsInfoGroup, HyundaiKiaVehicleDTO analyseResult) {
        return partsInfoGroup.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
                entry -> String.join(",", entry.getValue().stream()
                        .filter(d -> StringUtils.isNumeric(d.getCount()))
                        .filter(d -> StringUtils.isNotBlank(d.getCount()))
                        .map(d -> Integer.parseInt(d.getCount()) + "")
                        .collect(Collectors.toSet()))));
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        BASE_IMAGE_URL = appConfigLoader.getPartsPicBaseUrl();
    }

    @Override
    protected List<EPCPartsDTO> getEPCPartsDTOListFromBtrPartsByPartsName(Set<String> btrPartsNames, HyundaiKiaVehicleDTO analyseResult) {
        if (CollectionUtils.isEmpty(btrPartsNames) || analyseResult == null) {
            return Collections.emptyList();
        }
        //非油类自编码
        Set<HyundaiBtrPartsDTO> btrPartsInfo = queryBtrParts(btrPartsNames, analyseResult);
        if (btrPartsInfo.stream().anyMatch(Objects::isNull)) {
            log.warn("btrPartsInfo: {}", btrPartsInfo);
        }

        //油类自编码
        Set<HyundaiBtrPartsDTO> btrPartsInfoByOil = queryBtrOilParts(btrPartsNames, analyseResult);
        if (btrPartsInfoByOil.stream().anyMatch(Objects::isNull)) {
            log.warn("btrPartsInfoByOil: {}", btrPartsInfoByOil);
        }
        Set<HyundaiBtrPartsDTO> resultBtrPartsInfo = Sets.union(btrPartsInfo, btrPartsInfoByOil);
        if (CollectionUtils.isEmpty(resultBtrPartsInfo)) {
            return Collections.emptyList();
        }

        return resultBtrPartsInfo.stream()
                .filter(Objects::nonNull)
                .map(p -> {
                    EPCPartsDTO epcPartsDTO = new EPCPartsDTO();
                    epcPartsDTO.setBtrPartsName(p.getBtrPartsName());
                    epcPartsDTO.setPartsCode(p.getPartsCode());
                    if (p.isHasImg()) {
                        EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();
                        epcPartsImageDTO.setImageName(p.getPartsCode());
                        String imageUrl = "oil/" + p.getPartsCode() + ".png";
                        String imgPath = appConfigLoader.getEPCPartsImageFullUrl(EPCAnalyserEnum.HYUNDAI, imageUrl);
                        epcPartsImageDTO.setImageUrl(imgPath);
                        epcPartsDTO.setEpcPartsImageDTOs(Lists.newArrayList(epcPartsImageDTO));
                    }
                    return epcPartsDTO;
                }).collect(Collectors.toList());
    }

    private Set<HyundaiBtrPartsDTO> queryBtrOilParts(Set<String> btrPartsNames, HyundaiKiaVehicleDTO analyseResult) {
        return hyundaiBtrPartsDAO.querySelfPartsCode(btrPartsNames);
    }

    private Set<HyundaiBtrPartsDTO> queryBtrParts(Set<String> btrPartsNames, HyundaiKiaVehicleDTO analyseResult) {
        Set<HyundaiBtrPartsDTO> gmBtrPartsDTOS = hyundaiBtrPartsDAO.queryBtrPartsNameByBrand(analyseResult.getBrandName(), btrPartsNames);
        Set<String> btrPartsName = gmBtrPartsDTOS.stream().map(HyundaiBtrPartsDTO::getBasicPartsName).collect(Collectors.toSet());

        List<HyundaiKiaPartsMappingDTO> partsMappings = queryPartsMappingsForCall(btrPartsName, analyseResult);
        List<KiaPartsDTO> partsInfo = queryPartsInfoByPartsMappingForCall(partsMappings, analyseResult);
        partsInfo = filterPartsInfoByVinAnalyseResultForCall(partsInfo, analyseResult);
        Map<String, List<KiaPartsDTO>> partsInfoGroupByPartsName = getPartsInfoGroupByPartsNameForCall(partsInfo, partsMappings);
        partsInfoGroupByPartsName = filterPartsInfoByVinAnalyseResultForCall(partsInfoGroupByPartsName, analyseResult);
        List<EPCPartsDTO> epcPartsList = getEPCPartsDTOListForCall(partsInfoGroupByPartsName, analyseResult, null);
        if (CollectionUtils.isEmpty(epcPartsList)) {
            return Collections.emptySet();
        }
        Map<String, List<EPCPartsDTO>> partsMap = epcPartsList.stream().collect(Collectors.groupingBy(EPCPartsDTO::getBtrPartsName));
        return gmBtrPartsDTOS.stream()
                .filter(p -> CollectionUtils.isNotEmpty(partsMap.get(p.getBasicPartsName())))
                .flatMap(p -> {
                    List<EPCPartsDTO> epcPartsDTOS = partsMap.get(p.getBasicPartsName());
                    return epcPartsDTOS.stream().map(l ->
                            HyundaiBtrPartsDTO.builder()
                                    .btrPartsName(p.getBtrPartsName())
                                    .partsCode(l.getPartsCode() + p.getDistinguishingMarks() + p.getSuffix())
                                    .build()
                    );
                }).collect(Collectors.toSet());
    }

    @Override
    protected Map<String, Set<String>> queryFeatures(Map<String, List<KiaPartsDTO>> partsInfoGroup, HyundaiKiaVehicleDTO analyseResult) {
        Set<String> vehicleCode = partsInfoGroup.entrySet().stream()
                .flatMap(entry -> entry.getValue().stream())
                .flatMap(d -> d.getFeatureCodesJson().stream())
                .collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(vehicleCode)) {
            return Collections.emptyMap();
        }
        Map<String, List<HyundaiKiaFeatureEntity>> featuresGroupByCode = StreamKit.partitionStream(vehicleCode, SEGMENT_QUERY_SIZE)
                .flatMap(l -> featureDAO.queryFeatures(l).stream())
                .collect(Collectors.groupingBy(HyundaiKiaFeatureEntity::getCode));

        Map<String, Set<String>> result = Maps.newHashMap();
        for (Map.Entry<String, List<KiaPartsDTO>> entry : partsInfoGroup.entrySet()) {
            Set<String> partsFeature = entry.getValue().stream()
                    .flatMap(d -> d.getFeatureCodesJson().stream())
                    .collect(Collectors.toSet());
            if (CollectionUtils.isEmpty(partsFeature)) {
                continue;
            }
            result.put(entry.getKey(), partsFeature.stream().map(featuresGroupByCode::get)
                    .filter(CollectionUtils::isNotEmpty)
                    .flatMap(Collection::stream)
                    .map(HyundaiKiaFeatureEntity::getFeature)
                    .collect(Collectors.toSet()));
        }
        return result;
    }

    @Override
    protected Map<String, Set<String>> queryMaterial(Map<String, List<KiaPartsDTO>> partsInfoGroup, HyundaiKiaVehicleDTO analyseResult) {
        Set<String> vehicleCode = partsInfoGroup.entrySet().stream()
                .flatMap(entry -> entry.getValue().stream())
                .flatMap(d -> d.getFeatureCodesJson().stream())
                .collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(vehicleCode)) {
            return Collections.emptyMap();
        }
        Map<String, List<HyundaiKiaFeatureEntity>> featuresGroupByCode = StreamKit.partitionStream(vehicleCode, SEGMENT_QUERY_SIZE)
                .flatMap(l -> featureDAO.queryMaterial(l).stream())
                .collect(Collectors.groupingBy(HyundaiKiaFeatureEntity::getCode));

        Map<String, Set<String>> result = Maps.newHashMap();
        for (Map.Entry<String, List<KiaPartsDTO>> entry : partsInfoGroup.entrySet()) {
            Set<String> partsFeature = entry.getValue().stream()
                    .flatMap(d -> d.getFeatureCodesJson().stream())
                    .collect(Collectors.toSet());
            if (CollectionUtils.isEmpty(partsFeature)) {
                continue;
            }
            result.put(entry.getKey(), partsFeature.stream().map(featuresGroupByCode::get)
                    .filter(CollectionUtils::isNotEmpty)
                    .flatMap(Collection::stream)
                    .map(HyundaiKiaFeatureEntity::getMaterial)
                    .collect(Collectors.toSet()));
        }
        return result;
    }


    @Override
    protected List<PartsAssemblyDTO> queryPartsAssembly(Set<String> imageCodes, Set<String> partsNames, HyundaiKiaVehicleDTO analyseResult) {
        if (CollectionUtils.isEmpty(imageCodes) || CollectionUtils.isEmpty(partsNames) || Objects.isNull(analyseResult) || StringUtils.isBlank(analyseResult.getBpno())) {
            return Collections.emptyList();
        }
        return hyundaiKiaAssemblyDAO.queryPartsAssembly(imageCodes, partsNames, analyseResult.getBpno()).stream()
                .map(d -> (PartsAssemblyDTO) d)
                .collect(Collectors.toList());
    }
}

